/*
 * @(#)XfdmProcess.java        2003/01/01
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfdm.model;

import xfuzzy.lang.*;
import xfuzzy.xfds.*;

/**
 * Hilo del proceso de extracci�n de conocmiento
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XfdmThread implements Runnable {

	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Configuraci�n del proceso de extracci�n de conocimiento
	 */
	private XfdmConfig config;

	//----------------------------------------------------------------------------//
	//                               CONSTRUCTOR                                  //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor
	 */
	public XfdmThread(XfdmConfig config) {
		this.config = config;
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Ejecuci�n del hilo
	 */
	public void run() {
		try { exec(); }
		catch(Exception ex) {/**/ ex.printStackTrace(); }
	}

	/**
	 * Ejecuci�n del hilo
	 */
	public void exec() {
		XfdmAlgorithm algorithm = config.getAlgorithm();
		Specification spec = config.getSpecification();
		Operatorset opset = createOperatorSet(config);
		spec.addOperatorset(opset);
		
		Type[] inputtype = createInputTypes(config);
		for(int i=0; i<inputtype.length; i++) spec.addType(inputtype[i]);			
		if(algorithm.needInputMemFuncs()) createInputMemFuncs(config,inputtype);
		
		Type[] outputtype = createOutputTypes(config);
		for(int i=0; i<outputtype.length; i++) spec.addType(outputtype[i]);
		if(algorithm.needOutputMemFuncs()) createOutputMemFuncs(config,outputtype,inputtype);

		int kind = algorithm.getKnowledgeBaseKind();
		KnowledgeBase base =  createEmptyKnowledgeBase(config,kind,inputtype,outputtype,opset);
		spec.addRulebase(base);
		
		if(algorithm.needSystemStructure()) createSystemStructure(config,spec,inputtype,outputtype,base); 
		
		algorithm.extractKnowlegde(base,config);
		
		if(!algorithm.needSystemStructure() && config.getSystemStyle().createSystem()) {
			createSystemStructure(config,spec,inputtype,outputtype,base); 
		}
		
		XfdmEvaluation.evaluate(spec,config.getDataSet(),config.getDataTest(),config.getLogFile());		
		spec.setModified(true);
	}
	
	//----------------------------------------------------------------------------//
	//                             M�TODOS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Genera el conjunto de operadores de la base de conocimiento
	 */
	private Operatorset createOperatorSet(XfdmConfig config) {
		XfdmSystemStyle style = config.getSystemStyle();
		Specification spec = config.getSpecification();
		if(style == null) return null;

		String prefix = style.getKnowledgeBase()+"_opset";
		String oname = ""+prefix;
		int osi=0;
		while(spec.searchOperatorset(oname) != null) { oname = prefix+osi; osi++; }

		Operatorset op = new Operatorset(oname);

		switch(style.getAndOperator()) {
			case XfdmSystemStyle.MIN:
				op.set(new xfuzzy.pkg.xfl.binary.min(), FuzzyOperator.AND);
				break;
			case XfdmSystemStyle.PROD:
				op.set(new xfuzzy.pkg.xfl.binary.prod(), FuzzyOperator.AND);
				break;
		}

		switch(style.getDefuzMethod()) {
			case XfdmSystemStyle.FUZZYMEAN:
				op.set(new xfuzzy.pkg.xfl.defuz.FuzzyMean(),FuzzyOperator.DEFUZMETHOD);
				break;
			case XfdmSystemStyle.WEIGHTED:
				op.set(new xfuzzy.pkg.xfl.defuz.WeightedFuzzyMean(),FuzzyOperator.DEFUZMETHOD);
				break;
			case XfdmSystemStyle.TAKAGI:
				op.set(new xfuzzy.pkg.xfl.defuz.TakagiSugeno(),FuzzyOperator.DEFUZMETHOD);
				break;
			case XfdmSystemStyle.CLASSIFICATION:
				op.set(new xfuzzy.pkg.xfl.defuz.MaxLabel(),FuzzyOperator.DEFUZMETHOD);
				break;
		}

		return op;
	}
	
	/**
	 * Genera el conjunto de tipos de variables de entrada
	 */
	private Type[] createInputTypes(XfdmConfig config) {
		XfdsDataSet dataset = config.getDataSet();
		XfdsAttribute[] attr = dataset.getInputAttributes();
		Type[] type = new Type[attr.length];
		for(int i=0; i<attr.length; i++) {
			Universe universe = attr[i].getUniverse();
			String typename = "T"+attr[i].getName();
			type[i] = new Type(typename,universe);
		}
		return type;
	}

	/**
	 * Genera un conjunto de MFs predefinidas para los tipos de entrada
	 */
	private void createInputMemFuncs(XfdmConfig config, Type[] type) {
		XfdmInputStyle commonstyle = config.getCommonInputStyle();
		XfdmInputStyle[] inputstyle = config.getInputStyles();
		int mfs_default = commonstyle.getNumberOfMFs();
		int style_default = commonstyle.getStyle();
		
		for(int i=0; i<type.length; i++) {
			int mfs = mfs_default;
			int style = style_default;
			if(inputstyle != null && inputstyle.length > i && inputstyle[i] != null) {
				mfs = inputstyle[i].getNumberOfMFs();
				style = inputstyle[i].getStyle();
			}

			switch(style) {
				case XfdmInputStyle.FREE_TRIANGLES:
					TypeFactory.createFreeTriangles(type[i],mfs);
					break;
				case XfdmInputStyle.FREE_SH_TRIANGLES:
					TypeFactory.createFreeShoulderedTriangles(type[i],mfs);
					break;
				case XfdmInputStyle.FREE_GAUSSIANS:
					TypeFactory.createFreeGaussians(type[i],mfs);
					break;
				case XfdmInputStyle.TRIANGULAR_FAMILY:
					TypeFactory.createTriangularFamily(type[i],mfs);
					break;
				case XfdmInputStyle.SH_TRIANGULAR_FAMILY:
					TypeFactory.createShoulderedTriangularFamily(type[i],mfs); 
					break;
				case XfdmInputStyle.BSPLINES_FAMILY:
					TypeFactory.createBsplinesFamily(type[i],mfs);
					break;
			}
		}
	}

	/**
	 * Genera el conjunto de tipos de variables de salida	
	 */
	private Type[] createOutputTypes(XfdmConfig config){
		XfdsAttribute[] oattr = config.getDataSet().getOutputAttributes();
		Type otp[] = new Type[oattr.length];

		for(int i=0; i<oattr.length; i++) {
			String tname = "T"+oattr[i].getName();
			Universe universe = oattr[i].getUniverse();
			otp[i] = new Type(tname,universe);
		}
		return otp;
	}

	/**
	 * Genera el conjunto de tipos de variables de salida	
	 */
	private void createOutputMemFuncs(XfdmConfig config, Type[] otp, Type[] itp){
		int defuz = config.getSystemStyle().getDefuzMethod();
		switch(defuz) {
			case XfdmSystemStyle.FUZZYMEAN:
				int omfs1 = computeGridSize(itp);
				for(int i=0; i<otp.length; i++)	TypeFactory.createCenteredSingletons(otp[i],omfs1);
				break;
			case XfdmSystemStyle.WEIGHTED:
				int omfs2 = computeGridSize(itp);
				for(int i=0; i<otp.length; i++)	TypeFactory.createCenteredBells(otp[i],omfs2);
				break;
			case XfdmSystemStyle.TAKAGI:
				int omfs3 = computeGridSize(itp);
				for(int i=0; i<otp.length; i++)	TypeFactory.createTakagi(otp[i],omfs3,1+itp.length);
				break;
			case XfdmSystemStyle.CLASSIFICATION:
				XfdsAttribute[] oattr = config.getDataSet().getOutputAttributes();
				for(int i=0; i<otp.length; i++) {
					String[] value = ((XfdsEnumDataType)oattr[i].getType()).getValues();
					TypeFactory.createClasses(otp[i],value);
				}
				break;
		}		
	}

	/**
	 * Calcula el n�mero de reglas del grid 
	 */
	private int computeGridSize(Type[] itp) {
		int number=1;
		for(int i=0; i<itp.length; i++) {
			number *= itp[i].getAllMembershipFunctions().length;
		}
		return number;
	}

	/**
	 * Genera un conjunto de reglas vac�o 
	 */
	private KnowledgeBase createEmptyKnowledgeBase(XfdmConfig config, int kind, Type[]itp, Type[]otp, Operatorset opset) {
		String name = config.getSystemStyle().getKnowledgeBase();
		XfdsDataSet dataset = config.getDataSet();
		XfdsAttribute[] iattr = dataset.getInputAttributes();
		XfdsAttribute[] oattr = dataset.getOutputAttributes();
		
		KnowledgeBase base = null;
		switch(kind) {
			case KnowledgeBase.RULE_SET: base = new RuleSet(name); break;
			case KnowledgeBase.RULE_LIST: base = new RuleList(name); break;
		}

		base.setOperatorset(opset);
		Variable ivar[] = new Variable[iattr.length];
		Variable ovar[] = new Variable[oattr.length];
		for(int i=0; i<ivar.length; i++) {
			String varname = iattr[i].getName();
			ivar[i] = new Variable(varname,itp[i],Variable.INPUT);
			base.addInputVariable(ivar[i]);
		}
		for(int i=0; i<ovar.length; i++) {
			String varname = oattr[i].getName();
			ovar[i] = new Variable(varname,otp[i],base);
			base.addOutputVariable(ovar[i]);
		}
		return base;
	}	
	
	/**
	 * Genera la estructura modular
	 */
	private void createSystemStructure(XfdmConfig config,Specification spec, Type[] itp, Type[] otp, KnowledgeBase base) {
		XfdsAttribute[] iattr = config.getDataSet().getInputAttributes();
		XfdsAttribute[] oattr = config.getDataSet().getOutputAttributes();
		
		SystemModule system = spec.getSystemModule();
		RuleSetCall rmcall[] = system.getRulebaseCalls();
		for(int i=0; i<rmcall.length; i++) system.removeCall(rmcall[i]);
		Variable rmvar[] = system.getVariables();
		for(int i=0; i<rmvar.length; i++) system.removeVariable(rmvar[i]);
		
		for(int i=0; i<itp.length; i++) {
			system.addVariable( new Variable(iattr[i].getName(),itp[i],Variable.INPUT) );
		}
		for(int i=0; i<otp.length; i++) {
			system.addVariable( new Variable(oattr[i].getName(),otp[i],Variable.OUTPUT) );
		}
		system.addCall(base,system.getInputs(),system.getOutputs());
	}

}
